﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;

namespace Algorithms.Expressions
{
    static class DefaultExpressionFactoryLoader
    {
        public static IEnumerable<IExpressionFactory> Load()
        {
            return LoadConstantExpressions().Union(
                   LoadUnaryExpressions()).Union(
                   LoadBinaryExpressions());
        }

        private static IEnumerable<IExpressionFactory> LoadBinaryExpressions()
        {
            yield return new BinaryExpressionFactory("+", (left, right) => Expression.Add(left, right));
            yield return new BinaryExpressionFactory("-", (left, right) => Expression.Subtract(left, right));
            yield return new BinaryExpressionFactory("*", (left, right) => Expression.Multiply(left, right));
            yield return new BinaryExpressionFactory("/", (left, right) => Expression.Divide(left, right));
            yield return new BinaryExpressionFactory("^", (left, right) => Expression.Power(left, right));
            yield return new BinaryExpressionFactory("log", (left, right) => Expression.Call(typeof(Math).GetMethod("Log"), left, right));
        }

        private static IEnumerable<IExpressionFactory> LoadUnaryExpressions()
        {
            yield return new UnaryExpressionFactory("tan", (input) => Expression.Call(typeof(Math).GetMethod("Tan"), input));
            yield return new UnaryExpressionFactory("tanh", (input) => Expression.Call(typeof(Math).GetMethod("Tanh"), input));
            yield return new UnaryExpressionFactory("sin", (input) => Expression.Call(typeof(Math).GetMethod("Sin"), input));
            yield return new UnaryExpressionFactory("sinh", (input) => Expression.Call(typeof(Math).GetMethod("Sinh"), input));
            yield return new UnaryExpressionFactory("cos", (input) => Expression.Call(typeof(Math).GetMethod("Cos"), input));
            yield return new UnaryExpressionFactory("cosh", (input) => Expression.Call(typeof(Math).GetMethod("Cosh"), input));
        }

        private static IEnumerable<IExpressionFactory> LoadConstantExpressions()
        {
            yield return new ConstantExpressionFactory("x", ExpressionFactoryContainer.DefaultInputParameterExpression);
            yield return new ConstantExpressionFactory("PI", Math.PI);
            
            // add a default range of products (0 - 9)
            for (int i = 0; i < 10; i++)
            {
                yield return new ConstantExpressionFactory(i.ToString(), i);
            }
        }
    }
}
